/*
 *  main.cpp
 *  RiskAI
 *
 */

#include <stdio.h>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>

#include "Board.h"

#include "Player.h"
#include "HumanPlayer.h"
#include "SuperAggroAI.h"
#include "WandererAI.h"
#include "BullyAI.h"
#include "DistributorAI.h"
#include "OcdAI.h"
#include "BorderPatrolAI.h"

using namespace std;




//**********************************************************************************
//				GLOBAL VARIABLES
//**********************************************************************************

// Paramaters of the game loaded from config
static int    NUM_PLAYERS;
static bool   HUMAN_PLAYERS;
static bool   RANDOMIZED_PLACEMENT;
static bool   LEARNING;
static string BOARD_FILE;

//==================================================================================

// The board used by the game currently running
static CBoard* g_pxBoard = NULL;

// The vector of players currently in the game
static vector<string>   g_vsPlayerTypes;
static vector<CPlayer*> g_vpxPlayers;






//**********************************************************************************
//				SETUP APPLICATION
//**********************************************************************************

void DefaultSettings()
{
	NUM_PLAYERS = 1;
	HUMAN_PLAYERS = false;
	RANDOMIZED_PLACEMENT = true;
	LEARNING = false;
	BOARD_FILE = "../../boards/small/test.txt";
}

//==================================================================================

void ReadConfigFile()
{
	ifstream input("../../config.txt");
	if(input.is_open())
	{
		while(true)
		{
			string sElem;
			input >> sElem;
			if(input.fail()) break;
			
			if(sElem == "BOARD_FILE:")
			{
				ostringstream o;
				o << "../../boards/";
				string filename;
				input >> filename;
				o << filename;
				BOARD_FILE = o.str();
			}
			else if(sElem == "RANDOMIZED_PLACEMENT:")
			{
				string boolean;
				input >> boolean;
				RANDOMIZED_PLACEMENT = (boolean == "true" ? true : false);
			}
			else if(sElem == "LEARNING:")
			{
				string boolean;
				input >> boolean;
				LEARNING = (boolean == "true" ? true : false);
			}
			else if(sElem == "PLAYERS:")
			{
				input >> NUM_PLAYERS;
				
				g_vsPlayerTypes.clear();
				for(int i=0; i<NUM_PLAYERS; i++)
				{
					string sPlayerType;
					input >> sPlayerType;
					if(sPlayerType == "HUMAN") HUMAN_PLAYERS = true;
					g_vsPlayerTypes.push_back(sPlayerType);
				}
			}
		}
	}
	else
	{
		cout << endl << "Cant load config file" << endl;
	}
}

//==================================================================================

void SetupApp()
{
	DefaultSettings();
	ReadConfigFile();
}







//**********************************************************************************
//				SETUP GAME
//**********************************************************************************

void CreateBoard()
{
	if(g_pxBoard == NULL)
	{
		g_pxBoard = new CBoard(HUMAN_PLAYERS);
		
		if(!g_pxBoard->LoadFromFile(BOARD_FILE.c_str()))
		{
			cout << "EPIC FAIL: board can't load" << endl;
		}
	}
	g_pxBoard->WipeGame();
}

//==================================================================================

void CreatePlayer(int iIndex, string sPlayerType)
{
	CPlayer* pxPlayer = NULL;
	
	if(sPlayerType == "HUMAN")					pxPlayer = new CHumanPlayer(iIndex, g_pxBoard);
	else if(sPlayerType == "SUPER_AGGRO_AI")	pxPlayer = new CSuperAggroAI(iIndex, g_pxBoard);
	else if(sPlayerType == "WANDERER_AI")		pxPlayer = new CWandererAI(iIndex, g_pxBoard);
	else if(sPlayerType == "BULLY_AI")			pxPlayer = new CBullyAI(iIndex, g_pxBoard);
	else if(sPlayerType == "OCD_AI")			pxPlayer = new COcdAI(iIndex, g_pxBoard);
	else if(sPlayerType == "DISTRIBUTOR_AI")	pxPlayer = new CDistributorAI(iIndex, g_pxBoard);
	else if(sPlayerType == "BORDER_PATROL_AI")  pxPlayer = new CBorderPatrolAI(iIndex, g_pxBoard);
	
	if(pxPlayer != NULL)
		g_vpxPlayers.push_back(pxPlayer);
}

//==================================================================================

void CreatePlayers()
{

	// Clear out old players
	for(unsigned int i=0; i<g_vpxPlayers.size(); i++)
		delete g_vpxPlayers[i];
	g_vpxPlayers.clear();
	
	
	// Create new players
	for(unsigned int i=0; i<NUM_PLAYERS; i++)
		CreatePlayer(i, g_vsPlayerTypes[i]);
}

//==================================================================================

void ChooseTerritories(bool bRandom)
{
	if(bRandom)
	{
		g_pxBoard->AssignRandomOwnership(g_vpxPlayers);
	}
	else
	{
		if(HUMAN_PLAYERS)
		{
			cout << endl << "========================================" << endl;
			cout << "PART 1: Choosing Territories" << endl;
			cout << "========================================" << endl;
		}
		g_pxBoard->ChooseOwnership(g_vpxPlayers);
	}
}

//==================================================================================

void PlaceStartingArmies()
{
	if(HUMAN_PLAYERS)
	{
		for(int i=0; i<10; i++) cout << endl;
		cout << "========================================" << endl;
		cout << "PART 2: Placing Starting Armies" << endl;
		cout << "========================================" << endl << endl;
	}
	
	g_pxBoard->PlaceStartingArmies(g_vpxPlayers, RANDOMIZED_PLACEMENT);
}

//==================================================================================

void SetupGame()
{
	CreateBoard();
	CreatePlayers();

	ChooseTerritories(RANDOMIZED_PLACEMENT);	
	PlaceStartingArmies();
}






//**********************************************************************************
//				PLAYING THE GAME
//**********************************************************************************

void PrintGameState()
{
	cout << endl << "Current Player Standings:" << endl;
	
	for(unsigned int i=0; i<g_vpxPlayers.size(); i++)
		cout << g_vpxPlayers[i]->GetPlayerInfo() << endl;
	
	g_pxBoard->PrintBoardState();
}

//==================================================================================

void TroopBonus(int iPlayer)
{
	int iNumTroops = g_pxBoard->CalculateProduction(g_vpxPlayers[iPlayer]->GetTerritories());
	
	for(int i=iNumTroops; i>0; i--)
	{
		int iIndex = g_vpxPlayers[iPlayer]->AssignTroops(i);
		g_pxBoard->GetTerritory(iIndex)->AddArmies(1);
	}
}

//==================================================================================

void PrintPlayerTurn(int iPlayer)
{
	if(HUMAN_PLAYERS)
	{
		for(int i=0; i<10; i++) cout << endl;
		cout << "========================================" << endl;
		cout << "Player " << iPlayer << "\'s Turn" << endl;
		cout << "========================================" << endl << endl;
	}
}

//==================================================================================

int* RollDice(int iNumDice)
{
	int* aiDice = new int[3];
	
	for(int i=0; i<iNumDice; i++)
		aiDice[i] = rand() % 6 + 1;
	
	for(int i=iNumDice; i<3; i++)
		aiDice[i] = -1;
	
	if(aiDice[0] < aiDice[1])
	{
		int temp = aiDice[0];
		aiDice[0] = aiDice[1];
		aiDice[1] = temp;
	}
	
	if(aiDice[1] < aiDice[2])
	{
		int temp = aiDice[1];
		aiDice[1] = aiDice[2];
		aiDice[2] = temp;
	}
	
	if(aiDice[0] < aiDice[1])
	{
		int temp = aiDice[0];
		aiDice[0] = aiDice[1];
		aiDice[1] = temp;
	}
	
	return aiDice;
}

//==================================================================================

void PrintDice(int iPlayer, int* aiDice, int iNumDice)
{
	cout << "Player " << iPlayer << " rolls:";
	for(int i=0; i<iNumDice; i++)
		cout << " " << aiDice[i];
		
	cout << endl << endl;
}

//==================================================================================

void PrintAttack(int iAttacker, int iDefender, int* aiDice, RiskAction xAction)
{
	for(int i=0; i<10; i++) cout << endl;
	cout << "Player " << iAttacker << " attacks Player " << iDefender
		 << "\'s Territory #" << xAction.iAttackTarget << " from Territory #"
		 << xAction.iAttackSource << endl;
		 
	cout << g_pxBoard->GetTerritoryInfo(xAction.iAttackSource) << endl;
	cout << g_pxBoard->GetTerritoryInfo(xAction.iAttackTarget) << endl;

	PrintDice(iAttacker, aiDice, xAction.iNumDice);	
}

//==================================================================================

void ExecuteAction(int iAttacker, RiskAction xAction)
{
	int iDefender = g_pxBoard->GetTerritory(xAction.iAttackTarget)->GetOwner();
	
	int* aiAttackDice = RollDice(xAction.iNumDice);
	if(HUMAN_PLAYERS) PrintAttack(iAttacker, iDefender, aiAttackDice, xAction);
	
	int iNumDefenseDice = g_vpxPlayers[iDefender]->PickDefendingDie(aiAttackDice, xAction.iAttackTarget);
	
	int* aiDefenseDice = RollDice(iNumDefenseDice);
	if(HUMAN_PLAYERS) PrintDice(iDefender, aiDefenseDice, iNumDefenseDice);
	
	for(int i=0; i<iNumDefenseDice && i<xAction.iNumDice; i++)
	{
		int iLoser;
		if(aiAttackDice[i] > aiDefenseDice[i])
		{
			iLoser = iDefender;
			g_pxBoard->GetTerritory(xAction.iAttackTarget)->AddArmies(-1);
		}
		else
		{
			iLoser = iAttacker;
			g_pxBoard->GetTerritory(xAction.iAttackSource)->AddArmies(-1);
		}
		
		if(HUMAN_PLAYERS) cout << "Player " << iLoser << " loses 1 army." << endl;
	}
	
	if(g_pxBoard->GetTerritory(xAction.iAttackTarget)->GetNumArmies() < 1)
	{
		if(HUMAN_PLAYERS)
		{
			cout << endl << "PLAYER " << iAttacker << " HAS CONQUERED TERRITORY #" 
				 << xAction.iAttackTarget << endl << endl;
		}
		g_pxBoard->GetTerritory(xAction.iAttackTarget)->SetOwner(iAttacker);
		g_pxBoard->GetTerritory(xAction.iAttackTarget)->SetNumArmies(xAction.iNumDice);
		g_pxBoard->GetTerritory(xAction.iAttackSource)->AddArmies(-xAction.iNumDice);
		g_vpxPlayers[iAttacker]->GainTerritory(xAction.iAttackTarget);
		g_vpxPlayers[iDefender]->LoseTerritory(xAction.iAttackTarget);
		
		if(g_pxBoard->PlayerHasWon(g_vpxPlayers[iAttacker])) return;
		
		int iTroopMovement = 
			g_vpxPlayers[iAttacker]->MoveTroops(xAction.iAttackSource, xAction.iAttackTarget);
			
		g_pxBoard->GetTerritory(xAction.iAttackSource)->AddArmies(-iTroopMovement);
		g_pxBoard->GetTerritory(xAction.iAttackTarget)->AddArmies(iTroopMovement);
	}
}

//==================================================================================

void Fortify(int iPlayer)
{
	vector<int> viFortifiers = g_pxBoard->GetViableFortifiers(g_vpxPlayers[iPlayer]);
	if(viFortifiers.size() < 1)
	{
		if(HUMAN_PLAYERS)
			cout << "Player " << iPlayer << " has no Fortification options." << endl;
		return; 
	}
	
	CFortification xFort = g_vpxPlayers[iPlayer]->FortifyTroops(viFortifiers);
	if(xFort == NO_FORTIFICATION) return;
	
	g_pxBoard->GetTerritory(xFort.iSource)->AddArmies(-xFort.iNumTroops);
	g_pxBoard->GetTerritory(xFort.iDestination)->AddArmies(xFort.iNumTroops);
}

//==================================================================================

void Acknowledge()
{
	string line;
	cout << "Type anything when done: ";
	cin >> line;
}

//==================================================================================

void PlayOneRound(int iPlayer)
{
	if(g_vpxPlayers[iPlayer]->StillInGame())
	{
		TroopBonus(iPlayer);
		
		while(true)
		{
			if(HUMAN_PLAYERS) PrintGameState();
			
			if(g_pxBoard->PlayerHasWon(g_vpxPlayers[iPlayer])) return;
			
			vector<int> viViableAttackers =
				g_pxBoard->GetViableAttackers(g_vpxPlayers[iPlayer]);
			
			if(viViableAttackers.size() < 1)
			{
				if(HUMAN_PLAYERS)
					cout << "Player " << iPlayer << " can no longer attack, and must end his turn." << endl;
				break;
			}
			
			RiskAction xAction = g_vpxPlayers[iPlayer]->DecideOnAction(viViableAttackers);
			if(xAction == END_TURN) break;
			ExecuteAction(iPlayer, xAction);
			
			//if(HUMAN_PLAYERS) Acknowledge();
		}
		
		Fortify(iPlayer);
	}
}

//==================================================================================

void PlayGame()
{
	int iPlayer = rand() % g_vpxPlayers.size();
		
	while(true)
	{
		iPlayer = (iPlayer + 1) % g_vpxPlayers.size();
		PrintPlayerTurn(iPlayer);
		PlayOneRound(iPlayer);
		if(g_pxBoard->PlayerHasWon(g_vpxPlayers[iPlayer])) 
		{
			cout << endl << endl << "PLAYER " << iPlayer << " HAS WON!!" << endl;
			break;
		}
	}
}

//==================================================================================

int main(int argc, const char* argv[])
{	
	srand(time(NULL));
	
	SetupApp();
	
	//HUMAN_PLAYERS = true;
	for(int i=0; i<1; i++)
	{
		SetupGame();
		PlayGame();
	}
	
	return 0;
}